#include <ctype.h>
#include <dirent.h>
// #include <grp.h>
// #include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define PROC_NAME_LEN 64 // 最大进程名称
#define INIT_PROCS 50    // 初始进程的个数
#define MAX_LINE 256

static int max_procs, delay = 3, iterations;
static struct proc_info **old_procs, **new_procs; // 维护旧的进程和新的进程的链表
static int num_old_procs, num_new_procs;          //
static struct proc_info *free_procs;
static int num_used_procs, num_free_procs;

// 打印错误信息，退出进程
#define err_exit(...)                 \
    {                                 \
        fprintf(stderr, __VA_ARGS__); \
        exit(EXIT_FAILURE);           \
    }

// 进程结构体
struct proc_info
{
    struct proc_info *next;
    char state;
    pid_t pid;
    char name[PROC_NAME_LEN];
    long unsigned utime;
    long unsigned stime;
    long unsigned delta_utime;
    long unsigned delta_stime;
    long unsigned delta_time;
};


// 维护的进程链表
struct proc_list
{
    struct proc_info **array;
    int size;
};


// 申请一个进程结构
static void add_proc(int proc_num, struct proc_info *proc)
{
    int i;

    if (proc_num >= num_new_procs)
    {
        new_procs = realloc(new_procs, 2 * num_new_procs * sizeof(struct proc_info *));
        if (!new_procs)
            err_exit("Could not expand procs array.\n");
        for (i = num_new_procs; i < 2 * num_new_procs; i++)
            new_procs[i] = NULL;
        num_new_procs = 2 * num_new_procs;
    }
    new_procs[proc_num] = proc;
}


static struct proc_info *alloc_proc(void)
{
    struct proc_info *proc;

    if (free_procs)
    {
        proc = free_procs;
        free_procs = free_procs->next;
        num_free_procs--;
    }
    else
    {
        proc = malloc(sizeof(*proc));
        if (!proc)
            err_exit("Could not allocate struct process_info.\n");
    }

    num_used_procs++;

    return proc;
}


static void free_proc(struct proc_info *proc)
{
    proc->next = free_procs;
    free_procs = proc;

    num_used_procs--;
    num_free_procs++;
}


static void free_old_procs(void)
{
    int i;

    for (i = 0; i < num_old_procs; i++)
        if (old_procs[i])
            free_proc(old_procs[i]);

    free(old_procs);
}


static struct proc_info *find_old_proc(pid_t pid)
{
    int i;

    for (i = 0; i < num_old_procs; i++)
        if (old_procs[i] && (old_procs[i]->pid == pid))
            return old_procs[i];

    return NULL;
}


static int read_stat(char *filename, struct proc_info *proc)
{
    FILE *file;
    char buf[MAX_LINE], *ptr;

    file = fopen(filename, "r");
    if (!file)
        return 1;
    fgets(buf, MAX_LINE, file);
    fclose(file);

    ptr = strrchr(buf, ')');
    if (!ptr)
        return 1;

    sscanf(ptr + 1, " %c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
                    "%lu %lu",
           &proc->state, &proc->utime, &proc->stime);
    

    return 0;
}


static int read_cmdline(char *filename, struct proc_info *proc)
{
    FILE *file;
    char line[MAX_LINE];

    line[0] = '\0';
    file = fopen(filename, "r");
    if (!file)
        return 1;
    fgets(line, MAX_LINE, file);
    fclose(file);
    if (strlen(line) > 0)
    {
        strncpy(proc->name, line, PROC_NAME_LEN);
        proc->name[PROC_NAME_LEN - 1] = 0;
    }
    else
        proc->name[0] = 0;
    return 0;
}


static void read_procs(void)
{
    DIR *proc_dir;
    struct dirent *pid_dir, *tid_dir;
    char filename[64];
    FILE *file;
    int proc_num;
    struct proc_info *proc;
    pid_t pid, tid;

    int i;

    proc_dir = opendir("/proc");
    if (!proc_dir)
        err_exit("Could not open /proc.\n");

    new_procs = calloc(INIT_PROCS, sizeof(struct proc_info *));
    num_new_procs = INIT_PROCS;

    proc_num = 0;
    while ((pid_dir = readdir(proc_dir)))
    {
        if (!isdigit(pid_dir->d_name[0]))
            continue;

        pid = atoi(pid_dir->d_name);

        struct proc_info cur_proc;

        proc = alloc_proc();

        proc->pid = pid;

        sprintf(filename, "/proc/%d/stat", pid);
        read_stat(filename, proc);

        sprintf(filename, "/proc/%d/cmdline", pid);
        read_cmdline(filename, proc);

        add_proc(proc_num++, proc);
    }

    for (i = proc_num; i < num_new_procs; i++)
        new_procs[i] = NULL;

    closedir(proc_dir);
}


// 获取当前cpu的运行时间
long unsigned get_cpu_time(void)
{

    long unsigned utime, ntime, stime, itime;
    long unsigned iowtime, irqtime, sirqtime;
    long unsigned total_time;

    FILE *file;
    file = fopen("/proc/stat", "r");
    if (!file)
        err_exit("Could not open /proc/stat.\n");
    fscanf(file, "cpu %lu %lu %lu %lu %lu %lu %lu", &utime, &ntime, &stime,
           &itime, &iowtime, &irqtime, &sirqtime);
    fclose(file);
    total_time = utime + ntime + stime + itime + iowtime + irqtime + sirqtime;

    return total_time;
}



static void print_procs(long unsigned total_delta_time, double cpu_rate_watch)
{
    int i;
    struct proc_info *old_proc, *proc;

    for (i = 0; i < num_new_procs; i++)
    {
        if (new_procs[i])
        {
            old_proc = find_old_proc(new_procs[i]->pid);
            if (old_proc)
            {
                new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime;
                new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime;
            }
            else
            {
                new_procs[i]->delta_utime = 0;
                new_procs[i]->delta_stime = 0;
            }
            new_procs[i]->delta_time = new_procs[i]->delta_utime + new_procs[i]->delta_stime;
        }
    }


    for (i = 0; i < num_new_procs; i++)
    {
        proc = new_procs[i];

        if (!proc || (max_procs && (i >= max_procs)))
            break;
        if((((double)proc->delta_time) / total_delta_time) > cpu_rate_watch)
        printf("%5d %3ld%% %s\n", proc->pid, proc->delta_time * 100 / total_delta_time, proc->name);
    }
}




int main(int argc, char *argv[])
{
    long unsigned old_cpu_time, new_cpu_time, delta_cpu_time;
    double cpu_rate_watch = 0.1;

    if(argc==2){
        cpu_rate_watch = atof(argv[1]);
    }

    printf("%5s %4s %s\n", "PID", "CPU%", "Name");

    old_cpu_time = get_cpu_time();

    free_procs = NULL;
    num_new_procs = num_old_procs = 0;
    new_procs = old_procs = NULL;
    
    read_procs();

    while (1)
    {
        old_procs = new_procs;
        num_old_procs = num_new_procs;
        sleep(delay);
        new_cpu_time = get_cpu_time();
        delta_cpu_time = new_cpu_time - old_cpu_time;
        old_cpu_time = new_cpu_time;
        read_procs();
        print_procs(delta_cpu_time, cpu_rate_watch);
        free_old_procs();
    }
}